home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
UTILITIE
/
UNIX_COH
/
2774A.ZIP
/
LLESH.CZ
/
LLESH
Wrap
Text File
|
1991-07-02
|
16KB
|
602 lines
/* Shell Extension Program for UNIX Bourne Shell
Provides aliases, history, command editing, dir tree, & pathname prompt
Author: Thad Unrein
Date: Aug 10 1990
Source: /usr/thad/src/shsrc
Description:
Program produces a prompt, takes a string as a command line, parses
it for special tokens with strtok, then after taking action on any
special tokens it finds, resubmits the entire (ugh) string to
system() for execution. Of course the problem with this approach
is that even though the special actions provided are executed, when
they are resubmitted via system(), you get an error message; very ugly
but it works. (I have suggested some equally ugly solutions).
Parsed commands:
q == quit to top level shell
cd == change directory
lh == list history
prev [n] == repeat n commandline from history.
With no arg repeats most recent.
fc [n] == edit n commandline from history. Edits most recent with no arg.
alias [foo=bar] Establish alias. With no arg lists aliases.
eg alias frog='cd /usr' - frog will now change to directory /usr.
Revision History:
08/20/90 - added logic to allow commands after listing page of history (lh)
c == continue
q == quit
<n> == command number to re-execute
<CR> == continue
08/25/90 - added 'dt' command to list directory path names and change
to a numbered pathname - action and commands same as above
for 'lh' except that <n> cd's instead of re-executes.
09/01/90 - added numeric arg to lh
now if you type lh <n> where <n> is a number, lh will
begin listing history with that command line.
09/06/90 - changed 'r' command to 'prev' to avoid erroneous parsing
with commands that have 'r' options.
Bugs:
You must manually create .histfile and .aliasfile in your HOME directory
cat > .histfile
^D
will do it (same for .aliasfile)
I realize that having the program create the files if it doesn't find
them is easy enough, but frankly I'm tired of working on this thing.
Eventually I'll find elegant :-} solutions for these.
Errant error messages:
1. cd'ing to any directory where the arg
is not . , .. , or a full pathname. (Bad directory)
If this error message gets on your nerves (it did mine) define
NOBDERR at compile. This rewrites the command line to include a
full pathname for cd, but also precludes using cd in a compound
command string e.g. cd usr;lc.
2. lh, dt - can't find
To solve these (and compound the ugliness) I put dummy executables
for lh and dt in my /usr/local/bin (which is where 'shell' is).
Since the command line is completely parsed before being submitted
to system(), built in commands (lh, cd, dt, prev, etc) will be executed
before standard UNIX commands. Therefore the command line:
lc;lh;cd /usr
will be executed as:
lh;cd /usr;lc
Also, since everything is executed in a sub-shell via system, setting
variables (eg frog=hop) from the prompt won't work, since they dissap-
pear as soon as system returns.
For the same reason 'cd' has to be implemented as a system call in the
code; otherwise you just change directories for the subshell.
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#define BUFFER 256
#define TRUE 1
#define FALSE 0
struct Alias {
char Name[9];
char Line[81];
};
struct Alias *SavAlias[128];
char *DirStr[500]; /* Room for 500 pointers to directory strings */
char String[81];
char CmdLine[81];
char *CmdHist[128]; /* Limit history to previous 128 commands */
char *HistFilNam = "/.histfile";
char *AliasFilNam = "/.aliasfile";
char PathName[81]; /* Pathname for .histfile */
char APathName[81]; /* Pathname for .aliasfile */
char PromptName[81]; /* Stores PS1 to use as lead string for prompt so you
know if you are $ or root
*/
char EditFileName[17];
char HOMEBuf[BUFFER];
char CD[81];
FILE *HistFil;
FILE *AliasFil;
FILE *EditFile;
char *malloc();
int CmdCount, AliasCount = 1, i = 0;
int DirCount = 0, Page;
int ChDir;
/* Flags */
int RepeatFlag = 0;
FILE *ReadF(FilNam)
char *FilNam;
{
FILE *RF;
if((RF = fopen(FilNam, "r")) == NULL)
{
fprintf(stderr,"\nUnable to open %s for reading\n", FilNam);
exit(-1);
}
return(RF);
}
FILE *WriteF(FilNam)
char *FilNam;
{
FILE *WF;
if((WF = fopen(FilNam, "w")) == NULL)
{
fprintf(stderr,"\nUnable to open %s for writing\n", FilNam );
exit(-1);
}
return(WF);
}
GetDir(PathN)
char *PathN;
{
struct stat DirBuf;
if(stat(PathN, &DirBuf) == -1)
{
fprintf(stderr, "Can't access %s\n", PathN);
return;
}
if((DirBuf.st_mode & S_IFMT) == S_IFDIR)
{
ListDir(PathN);
++DirCount;
DirStr[DirCount] = malloc((sizeof(String)) + 1);
strcpy(DirStr[DirCount], PathN);
}
}
ListDir(PathN)
char *PathN;
{
struct direct DPath;
char *PathLen, *PN;
int i, FilD;
PathLen = PathN + strlen(PathN);
*PathLen++ = '/';
if(PathLen + DIRSIZ + 2 >= PathN + BUFSIZ)
return;
if((FilD = open(PathN, 0)) == -1)
return;
while(read(FilD, (char *)&DPath, sizeof(DPath)) > 0)
{
if(DPath.d_ino == 0)
continue;
if(strcmp(DPath.d_name, ".") == 0)
continue;
if(strcmp(DPath.d_name, "..") == 0)
continue;
PN = PathLen;
for(i = 0; i < DIRSIZ; i++)
*PN++ = DPath.d_name[i];
*PN++ = '\0';
GetDir(PathN);
}
close(FilD);
*--PathLen = '\0';
}
void Parse()
{
char *CmdPtr, separators[5] = " =;\n", Choice[4];
int ChCmd;
CmdPtr = strtok(CmdLine, separators);
while(CmdPtr != NULL)
{
#ifdef DEBUG
printf("Top of loop, CmdPtr = %s\n", CmdPtr);
#endif
if(strcmp(CmdPtr, "q") == 0)
{
#ifdef DEBUG
printf("In q, CmdPtr = %s\n", CmdPtr);
#endif
HistFil = WriteF(PathName);
#ifdef DEBUG
printf("PathName = %s\n", PathName);
#endif
for(i = 1; i <= CmdCount; ++i)
{
fputs(CmdHist[i], HistFil);
}
AliasFil = WriteF(APathName);
for(i = 1; i < AliasCount; ++i)
{
fputs(SavAlias[i] -> Name, AliasFil);
fputs(SavAlias[i] -> Line, AliasFil);
}
exit(0);
}
/* cd for CURRENT process */
else if(strcmp(CmdPtr, "cd") == 0)
{
CmdPtr = strtok(NULL, separators);
if(! CmdPtr)
chdir(getenv("HOME"));
#ifdef NOBDERR
else if(strncmp(CmdPtr, "/", 1) != 0)
{
strcpy(HOMEBuf, getwd());
strcat(HOMEBuf, "/");
strcat(HOMEBuf, CmdPtr);
chdir(HOMEBuf);
strcat(HOMEBuf, "\n");
strcpy(CD, "cd ");
strcat(CD, HOMEBuf);
CmdHist[CmdCount] = malloc(sizeof(CD) + 1);
strcpy(CmdHist[CmdCount], CD);
HOMEBuf[0] = '\0';
CD[0] = '\0';
return;
}
else
#endif
chdir(CmdPtr);
}
else if(strcmp(CmdPtr, "dt") == 0)
{
CmdPtr = strtok(NULL, separators);
if(CmdPtr == NULL)
{
strcpy(HOMEBuf, getwd());
CmdPtr = malloc(sizeof(HOMEBuf) + 1);
strcpy(CmdPtr, HOMEBuf);
}
printf("Loading Pathnames\n");
GetDir(CmdPtr);
i = 1;
while(i <= DirCount)
{
printf("%d\t%s\n",i, DirStr[i]);
if(i == DirCount || (i % 23) == 0)
{
start:
fflush(stdin);
printf(": ");
gets(Choice);
switch(Choice[0])
{
case 'c':break;
case 'q':goto finish;
case '?':
printf("c)ontinue, q)uit, <Dir Number> to chdir");
goto start;
default:
if((isdigit(Choice[0])) != 0)
{
ChDir = atoi(Choice);
/* DirStr[DirCount] = malloc(strlen(DirStr[ChDir]) + 1);
strcpy(DirStr[DirCount], DirStr[ChDir]);
*/
if((chdir(DirStr[ChDir])) != 0)
fprintf(stderr, "Can't change to that %s\n", DirStr[ChDir]);
goto finish;
}
else
break;
}
}
++i;
}
finish:;
DirCount = 0;
}
/* lh lists history of commands stored in CmdHist */
/* a screenfull at a time */
else if(strcmp(CmdPtr, "lh") == 0)
{
/* check for a numeric argument to lh; begin listing with that command string */
/* from history list */
/* get next token */
CmdPtr = strtok(NULL, separators);
/* is next token a numeral? */
if((i = atoi(CmdPtr)) != 0)
;
else
i = 1;
while(i <= CmdCount)
{
printf("%d\t%s",i, CmdHist[i]);
if(i == CmdCount || (i % 23) == 0)
{
Page = 0;
begin:
fflush(stdin);
printf(": ");
gets(Choice);
switch(Choice[0])
{
case 'c':break;
case 'q':
goto end;
case '?':
printf("c)ontinue, q)uit, <Cmd Number> to re-execute");
goto begin;
default:
if((isdigit(Choice[0])) != 0)
{
ChCmd = atoi(Choice);
CmdHist[CmdCount] = malloc(strlen(CmdHist[ChCmd]) + 1);
strcpy(CmdHist[CmdCount], CmdHist[ChCmd]);
RepeatFlag = 1;
goto end;
}
else
break;
}
}
++i;
}
end:;
}
/* Let's go for broke - Aliases!! */
else if(strcmp(CmdPtr, "alias") == 0)
{
#ifdef DEBUG
printf("in alias, CmdPtr = %s\n", CmdPtr);
#endif
CmdPtr = strtok(NULL, separators); /* advance CmdPtr */
if(! CmdPtr) /* no args to alias */
for(i = 1; i < AliasCount; ++i) /* list current aliases */
printf("%s\t%s", SavAlias[i] -> Name, SavAlias[i] -> Line);
else
{ /* establish alias */
SavAlias[AliasCount] = malloc(sizeof(struct Alias));
#ifdef DEBUG
printf("Before Name, CmdPtr = %s\n", CmdPtr);
#endif
strcpy(SavAlias[AliasCount] -> Name, CmdPtr);
strcat(SavAlias[AliasCount] -> Name, "\n");
CmdPtr = strtok(NULL, "\'\"");
#ifdef DEBUG
printf("Before Line, CmdPtr = %s\n", CmdPtr);
#endif
strcpy(SavAlias[AliasCount] -> Line, CmdPtr);
strcat(SavAlias[AliasCount] -> Line, "\n");
CmdPtr = strtok(NULL, separators);
++AliasCount;
}
}
else if(strcmp(CmdPtr, "prev") == 0)
{
i = 0;
#ifdef DEBUG
printf("in r, CmdPtr = %s\n", CmdPtr);
#endif
/* check for a numeric argument to prev and execute that command string */
/* from history list */
/* get next token */
CmdPtr = strtok(NULL, separators);
/* is next token a numeral? */
if((i = atoi(CmdPtr)) != 0)
{
CmdHist[CmdCount] = malloc(strlen(CmdHist[i]) + 1);
strcpy(CmdHist[CmdCount], CmdHist[i]);
}
/* if no argument execute previous command */
else
{
CmdHist[CmdCount] = malloc(strlen(CmdHist[CmdCount - 1]) + 1);
strcpy(CmdHist[CmdCount], CmdHist[CmdCount - 1]);
}
RepeatFlag = 1;
}
else if(strcmp(CmdPtr, "fc") == 0)
{
/* check for a numeric argument to fc and edit that command string */
/* from history list */
i = 0;
CmdPtr = strtok(NULL, separators);
/* is next token a numeral? */
strcpy(EditFileName, mktemp("/tmp/shellXXXXXX"));
EditFile = WriteF(EditFileName);
if((i = atoi(CmdPtr)) != 0)
{
fputs(CmdHist[i], EditFile);
fclose(EditFile);
if(fork() == 0)
execl("/bin/vi","vi",EditFileName, (char *) 0);
else
wait((int *) 0);
CmdHist[CmdCount] = malloc(81);
EditFile = ReadF(EditFileName);
fgets(CmdHist[CmdCount], 81, EditFile);
fclose(EditFile);
}
/* if no argument edit previous command */
else
{
fputs(CmdHist[CmdCount - 1], EditFile);
fclose(EditFile);
if(fork() == 0)
execl("/bin/vi","vi",EditFileName, (char *) 0);
else
wait((int *) 0);
CmdHist[CmdCount] = malloc(81);
EditFile = ReadF(EditFileName);
fgets(CmdHist[CmdCount], 81, EditFile);
fclose(EditFile);
}
if(unlink(EditFileName) != 0)
fprintf(stderr, "Can't unlink %s\n", EditFileName);
RepeatFlag = 1;
}
/* get next token */
CmdPtr = strtok(NULL, separators);
#ifdef DEBUG
printf("Bottom of loop, CmdPtr = %s\n", CmdPtr);
printf("Bottom of loop, CmdLine = %s\n", CmdLine);
#endif
}
}
main()
{
char CmdString[81];
CmdCount = 0;
/* open history file */
strcpy(PathName, getenv("HOME"));
strcpy(APathName, getenv("HOME"));
strcat(PathName, HistFilNam);
#ifdef DEBUG
printf("PathName for HistFile = %s\n", PathName);
#endif
HistFil = ReadF(PathName);
while(fgets(CmdString, 81, HistFil) != NULL)
{
++CmdCount;
CmdHist[CmdCount] = malloc(strlen(CmdString) + 1);
strcpy(CmdHist[CmdCount], CmdString);
}
fclose(HistFil);
strcat(APathName, AliasFilNam);
AliasFil = ReadF(APathName);
while(fgets(CmdString, 9, AliasFil) != NULL)
{
SavAlias[AliasCount] = malloc(sizeof(struct Alias));
strcpy(SavAlias[AliasCount] -> Name, CmdString);
fgets(CmdString, 81, AliasFil);
strcpy(SavAlias[AliasCount] -> Line, CmdString);
++AliasCount;
}
fclose(AliasFil);
while(1)
{
char directory[80];
strcpy(directory, getwd());
strcpy(PromptName, getenv("PS1"));
printf("%s%s [%d]>", PromptName, directory, CmdCount);
fgets(CmdLine, 81, stdin);
if(strcmp(CmdLine,"\n") != 0)
{
for(i = 1; i < AliasCount; ++i)
if(strncmp(CmdLine, SavAlias[i] -> Name, strlen(CmdLine)) == 0)
strcpy(CmdLine, SavAlias[i] -> Line);
++CmdCount;
}
if(CmdCount > 128)
CmdCount = 1;
CmdHist[CmdCount] = malloc(strlen(CmdLine) + 1);
strcpy(CmdHist[CmdCount], CmdLine); /* copy for history */
Parse();
if(RepeatFlag)
{
RepeatFlag = 0;
strcpy(CmdLine, CmdHist[CmdCount]);
printf("%s\n", CmdLine);
Parse();
strcpy(CmdLine, CmdHist[CmdCount]);
}
else
strcpy(CmdLine, CmdHist[CmdCount]); /* get unaltered CmdLine */
#ifdef DEBUG
printf("At system - CmdHist[CmdCount] = %s\n", CmdHist[CmdCount]);
#endif
system(CmdLine);
}
}